package com.hl.payServer.service.impl;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradePayResponse;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.hl.payServer.core.aliPay.AliPay;
import com.hl.payServer.core.wePay.*;
import com.hl.payServer.exception.WepayException;
import com.hl.payServer.mapper.PayTradeInfoMapper;
import com.hl.payServer.model.*;
import com.hl.payServer.model.enums.FeeType;
import com.hl.payServer.model.enums.TradeState;
import com.hl.payServer.model.enums.TradeType;
import com.hl.payServer.service.PayPropertyService;
import com.hl.payServer.service.PayTradeInfoService;
import com.hl.payServer.util.DateUtil;
import com.hl.payServer.util.Preconditions;
import com.hl.payServer.util.xml.XmlReaders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import java.io.File;
import java.io.FileInputStream;
import java.util.*;


@Service
public class PayTradeInfoServiceImpl implements PayTradeInfoService {

    private static final Logger logger = LoggerFactory.getLogger(PayTradeInfoServiceImpl.class);
    @Autowired
    private PayTradeInfoMapper payTradeInfoMapper;
    @Autowired
    private PayPropertyService payPropertyService;
    @Value("${project.aliNotifyUrl}")
    private String aliNotifyUrl;
    @Value("${project.weNotifyUrl}")
    private String weNotifyUrl;

    @Override
    public PageBean<HashMap<String,Object>> queryList(HashMap<String,Object> param) {
        PageBean<HashMap<String, Object>> pageBean = new PageBean<HashMap<String, Object>>(
                param.get("pageNum")==null?null:Integer.parseInt(param.get("pageNum").toString()),
                param.get("pageSize")==null?null:Integer.parseInt(param.get("pageSize").toString()));
        param.put("OffsetStart", pageBean.countOffsetStart());
        param.put("OffsetEnd", pageBean.countOffsetEnd());
        List<HashMap<String, Object>> list = payTradeInfoMapper.selectByParam(param);
        pageBean.setList(list);
        pageBean.countTotalPage(payTradeInfoMapper.selectCountByParam(param));
        return pageBean;
    }
    @Override
    public List<PayTradeInfo> queryListSelfUse(HashMap<String,Object> param) {
        List<PayTradeInfo> list = payTradeInfoMapper.queryListSelfUse(param);
        return list;
    }

    public PayTradeInfo findByTradeNo(String tradeNo) {
        Example example = new Example(PayTradeInfo.class);
        Example.Criteria criteria1 = example.createCriteria();
        criteria1.andEqualTo("tradeNo", tradeNo);
        return payTradeInfoMapper.selectOneByExample(example);
    }

    public PayTradeInfo checkAndSet(Map<String, Object> params, String data) {
        PayTradeInfo payTradeInfo = new PayTradeInfo();
        payTradeInfo.setTradeType((String) params.get("tradeType"));
        payTradeInfo.setPayChannle((Integer) params.get("payChannle"));
        payTradeInfo.setCompanyCode((String) params.get("companyCode"));
        payTradeInfo.setBizNo((String) params.get("bizNo"));
        payTradeInfo.setNotifieUrl((String) params.get("notifieUrl"));
        payTradeInfo.setBackup1((String)params.get("backup1"));
        payTradeInfo.setBackup2((String)params.get("backup2"));
        payTradeInfo.setBackup3((String)params.get("backup3"));
        payTradeInfo.setBackup4((String)params.get("backup4"));
        payTradeInfo.setBackup5((String)params.get("backup5"));
        payTradeInfo.setProjectCode((String)params.get("projectCode"));
        payTradeInfo.setBody((String)params.get("body"));
        payTradeInfo.setAmount((Integer)params.get("amount"));
        payTradeInfo.setBizData(data);
        payTradeInfo.initCreateTime();

        if (payTradeInfo.getAmount() == null || payTradeInfo.getAmount() <= 0) {
            throw new IllegalArgumentException("必填参数amount：参数(amount)必须为大于0的整数单位为分.");
        }
        Preconditions.checkNotNullAndEmpty(payTradeInfo.getTradeType(), "tradeType");
        Preconditions.checkPositive(payTradeInfo.getPayChannle(), "payChannle");
        Preconditions.checkNotNullAndEmpty(payTradeInfo.getCompanyCode(), "companyCode");
        Preconditions.checkNotNullAndEmpty(payTradeInfo.getBizNo(), "bizNo");
        Preconditions.checkNotNullAndEmpty(payTradeInfo.getNotifieUrl(), "notifieUrl");
        Preconditions.checkNotNullAndEmpty(payTradeInfo.getProjectCode(), "projectCode");
        Preconditions.checkNotNullAndEmpty(payTradeInfo.getBody(), "body");
        if(payTradeInfo.getTradeType().equals(TradeType.MINI.type())){
            if (params.get("openId") == null || params.get("openId").toString().equals("")) {
                throw new NullPointerException("必填参数为空：参数(openId)不能为空.");
            }
        }
        if (payTradeInfo.getTradeType().equals(TradeType.MICROPAY.type())) {
            if (params.get("authCode") == null || params.get("authCode").toString().equals("")) {
                throw new NullPointerException("必填参数为空：参数(authCode)不能为空.");
            }
        }
        return payTradeInfo;
    }

    @Override
    @Transactional(readOnly = false)
    public ResultModel pay(Map<String, Object> params, String data) {
        PayTradeInfo payTradeInfo = checkAndSet(params, data);
        payTradeInfo.setTradeNo(createTableNo("thirdPay", DateUtil.getNowTime("yyyyMMdd")));
        params.put("sendOrder", payTradeInfo.getTradeNo());
        if (payTradeInfoMapper.findByBizNo(payTradeInfo.getBizNo())!=null) {
            logger.error("bizNo订单编号已经存在."+data);
            throw new IllegalArgumentException("bizNo订单编号已经存在.");
        }
        PayProperty property = payPropertyService.selectByCompanyCodeAndPayChannle(payTradeInfo.getCompanyCode(), payTradeInfo.getPayChannle(),getPayType(payTradeInfo));
        if (property == null) {
            throw new IllegalArgumentException("获取公司支付配置信息失败.查无数据");
        }
        if (payTradeInfo.getPayChannle() == 1) {//支付宝
            return alipay(params, payTradeInfo, property);
        } else if (payTradeInfo.getPayChannle() == 2) {//微信
            return wepay(params, payTradeInfo, property);
        } else {
            logger.error("payChannle参数错误,当前支付通道不存在." + data);
            throw new IllegalArgumentException("payChannle参数错误,当前支付通道不存在.");
        }
    }

    private String getPayType(PayTradeInfo payTradeInfo) {
        String payType = null;
        if (payTradeInfo.getTradeType().equals(TradeType.MICROPAY.type()) || payTradeInfo.getTradeType().equals(TradeType.NATIVE.type())) {
            payType = "base";
        }else if(payTradeInfo.getTradeType().equals(TradeType.APP.type())){
            payType = "app";
        }else if(payTradeInfo.getTradeType().equals(TradeType.MINI.type())){
            payType = "mini";
        }
        return payType;
    }


    /**
     * 支付宝
     *
     * @param params
     * @param payTradeInfo
     * @return
     */
    private ResultModel alipay(Map<String, Object> params, PayTradeInfo payTradeInfo, PayProperty property) {
        PayResponse payResponse = new PayResponse();
        //组装配置
        AlipayClient alipayClient = aliPayBuild(property);
        //支付宝是以元为单位
        double money = Double.parseDouble(payTradeInfo.getAmount().toString()) / 100;
        params.put("payMoney", money);
        params.put("payNotifyUrl", aliNotifyUrl);
        if (payTradeInfo.getTradeType().equals(TradeType.MICROPAY.type())) {
            //刷卡支付
            AlipayTradePayResponse response = null;
            try {
                response = AliPay.microPay(alipayClient, params);
            } catch (AlipayApiException e) {
                logger.error("生成支付订单错误,调用第三方异常." + payTradeInfo.getBizData(), e);
                throw new NullPointerException("生成支付订单错误,调用第三方异常." + e.getMessage());
            }
            //成功{"body":"{\"alipay_trade_pay_response\":{\"code\":\"10000\",\"msg\":\"Success\",\"buyer_logon_id\":\"347***@qq.com\",\"buyer_pay_amount\":\"0.01\",\"buyer_user_id\":\"2088102146582094\",\"fund_bill_list\":[{\"amount\":\"0.01\",\"fund_channel\":\"POINT\"}],\"gmt_payment\":\"2017-12-01 11:07:00\",\"invoice_amount\":\"0.00\",\"out_trade_no\":\"201712010000000011\",\"point_amount\":\"0.01\",\"receipt_amount\":\"0.01\",\"total_amount\":\"0.01\",\"trade_no\":\"2017120121001004095391992786\"},\"sign\":\"mA0N2Bv4Bi4szhfxTYS4Ocd5Ozfky75n +vlL/S8P7oPtHrasSE7Gt59a2Pj7a0fqbOxFHbSVtbSzS5tiz6pFlDZloK00alg/Ko37LIO6BQq2uCH2jRHVI+3IZdXKnIuskV+Y5pD7CRrXYQF0garId4grYLpyxgu9xRbWDb8ev0k=\"}","buyerLogonId":"347***@qq.com","buyerPayAmount":"0.01","buyerUserId":"2088102146582094","code":"10000","errorCode":"10000","fundBillList":[{"amount":"0.01","fundChannel":"POINT"}],"gmtPayment":1512097620000,"invoiceAmount":"0.00","msg":"Success","outTradeNo":"201712010000000011","params":{"biz_content":"{\"auth_code\":\"287240683817548563\",\"body\":\"订单：SHDDD20171201003, 客户：杜雨微, 金额：发货应收(0.01)\",\"out_trade_no\":\"201712010000000011\",\"scene\":\"bar_code\",\"subject\":\"仓配一体化系统\",\"total_amount\":\"0.01\"}"},"pointAmount":"0.01","receiptAmount":"0.01","success":true,"totalAmount":"0.01","tradeNo":"2017120121001004095391992786"}
            //失败{"body":"{\"alipay_trade_pay_response\":{\"code\":\"40004\",\"msg\":\"Business Failed\",\"sub_code\":\"ACQ.INVALID_PARAMETER\",\"sub_msg\":\"支付失败，商品订单的商品标题不能为空，请顾客刷新付款码后重新收款。如再次收款失败，请联系管理员处理。[INVALID_PARAMETER]\",\"buyer_pay_amount\":\"0.00\",\"invoice_amount\":\"0.00\",\"point_amount\":\"0.00\",\"receipt_amount\":\"0.00\"},\"sign\":\"NwLPzjJZBpr2zfCwOGXTaGLDnxt5Zt/165jqE4p9gTsxYKRp16AJwyaxZpBH7m8XVL7oESEZiny1w8+Gp5G0KdlpKf+kMF4PUuievec5Vv8uCjsH7VEJo5kGcsBtQTXu7a7TqIioQQCDf01CZWgLYh8jmN0e6Qf8tsYg3eX6r3g=\"}","buyerPayAmount":"0.00","code":"40004","errorCode":"40004","invoiceAmount":"0.00","msg":"Business Failed","params":{"biz_content":"{\"auth_code\":\"285042961619328791\",\"body\":\"订单：SHDDD20171201003, 客户：杜雨微, 金额：发货应收(0.01)\",\"out_trade_no\":\"201712010000000010\",\"scene\":\"bar_code\",\"subject\":\"\",\"total_amount\":\"0.01\"}"},"pointAmount":"0.00","receiptAmount":"0.00","subCode":"ACQ.INVALID_PARAMETER","subMsg":"支付失败，商品订单的商品标题不能为空，请顾客刷新付款码后重新收款。如再次收款失败，请联系 管理员处理。[INVALID_PARAMETER]","success":false}
            payTradeInfo.setOutTradeNo(response.getTradeNo());
            payTradeInfo.setReturnDetailInfo(JSON.toJSONString(response));
            if ("10000".equals(response.getCode())) {
                //完全成功了   这种情况不用进行回调通知了
                payTradeInfo.setNotifieFlag(1);
                payTradeInfo.setPayTime(response.getGmtPayment());
                payTradeInfo.setStatus(TradeState.SUCCESS.type());
                payResponse.setSuccessPayResponse(payTradeInfo.getStatus(),payTradeInfo.getReturnDetailInfo());
            } else {
                //商家系统将用户付款码与订单信息一起通过 alipay.trade.pay(统一收单交易支付接口) 请求到支付宝，并从接口同步返回中获取支付结果。根据公共返回参数中的 code，
                // 这笔交易可能有四种状态：支付成功（10000），支付失败（40004），等待用户付款（10003）和未知异常（20000）。
                if ("10003".equals(response.getCode()) || "20000".equals(response.getCode())) {//待定时器查询   支付中
                    payTradeInfo.setStatus(TradeState.USERPAYING.type());
                    payResponse.setFailPayResponse(response.getSubCode(),"支付宝错误信息:"+response.getSubMsg()+".当前订单交易状态未知,需要等待支付宝确认后等待回调通知,也可以主动调用查询接口查询!",payTradeInfo.getStatus(),payTradeInfo.getReturnDetailInfo());
                } else {//失败
                    payTradeInfo.setStatus(TradeState.PAYERROR.type());
                    payResponse.setFailPayResponse(response.getSubCode(), response.getSubMsg(), payTradeInfo.getStatus(), payTradeInfo.getReturnDetailInfo());
                }
            }

        } else if (payTradeInfo.getTradeType().equals(TradeType.NATIVE.type())) {
            //扫码支付
            AlipayTradePrecreateResponse response = null;
            try {
                //{"body":"{\"alipay_trade_precreate_response\":{\"code\":\"10000\",\"msg\":\"Success\",\"out_trade_no\":\"test201810090000000004\",\"qr_code\":\"https:\\/\\/qr.alipay.com\\/bax067726ombid0e0kld4040\"},\"sign\":\"WvXFTxJ0Pu5rqWdhImEVin2IGYTk/T/c2G+Mht4oabZ0fPdDh5lni9xk36WUFlLAYdQghU3DzuBOoPtPW8rearg/WMKvsdIAQYCVbNA9J43G07Al1qjnfEaJy/UUypRrC6ZH/T2sSzXJDrGIhZpBf3al3hNJw88oNi+LbC1cf1c=\"}","code":"10000","errorCode":"10000","msg":"Success","outTradeNo":"test201810090000000004","params":{"biz_content":"{\"out_trade_no\":\"test201810090000000004\",\"total_amount\":0.01,\"subject\":\"fytest-yunfei\",\"timeout_express\":\"24h\",\"qr_code_timeout_express\":\"120m\"}"},"qrCode":"https://qr.alipay.com/bax067726ombid0e0kld4040","success":true}
                response = AliPay.qrPay(alipayClient, params);
            } catch (AlipayApiException e) {
                logger.error("生成支付订单错误,调用第三方异常." + payTradeInfo.getBizData(), e);
                throw new NullPointerException("生成支付订单错误,调用第三方异常." + e.getMessage());
            }
            //现在没有支付宝的tradeNo返回
            payTradeInfo.setReturnDetailInfo(JSON.toJSONString(response));
            if (response.isSuccess()) {
                payTradeInfo.setStatus(TradeState.USERPAYING.type());
                payResponse.setSuccessPayResponse(payTradeInfo.getStatus(),response.getQrCode());
            } else {
                payTradeInfo.setStatus(TradeState.PAYERROR.type());
                payResponse.setFailPayResponse(response.getSubCode(), response.getSubMsg(), payTradeInfo.getStatus(), payTradeInfo.getReturnDetailInfo());
            }
        } else if (payTradeInfo.getTradeType().equals(TradeType.APP.type())) {
            //APP支付   TODO 这块等账号申请下来,看一下,appID和公钥私钥是否一样,如果不一样需要在新加一条配置数据专与类型   分别为,线上,线下,小程序(微信要)
            AlipayTradeAppPayResponse response = null;
            try {
                response = AliPay.appPay(alipayClient, params);
            } catch (AlipayApiException e) {
                logger.error("生成支付订单错误,调用第三方异常." + payTradeInfo.getBizData(), e);
                throw new NullPointerException("生成支付订单错误,调用第三方异常." + e.getMessage());
            }
            //正经的返回值
            //{"body":"alipay_sdk=alipay-sdk-java-dynamicVersionNo&app_id=2016080901724807&biz_content=%7B%22body%22%3A%2200001%E5%95%86%E5%93%81%E7%AD%89%E5%95%86%E5%93%81%22%2C%22out_trade_no%22%3A%22201712010000000015%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22subject%22%3A%22%E5%95%86%E5%9F%8E%E4%B8%8B%E5%8D%95%E6%94%AF%E4%BB%98%22%2C%22total_amount%22%3A%220.01%22%7D&charset=utf-8&format=json&method=alipay.trade.app.pay&sign=jooRRQ8BBjJ57KMvYL0vlBrQfXPfopu1tq82TAjqJxBz9d8dddkEdwIGdZeklS7a09l9VTt02J3QLvRS37CpS5k6JfFuYPs%2BBlFs1T1fwYi%2BXITezrrfy%2F0959yE9OynPGIbiqKTb2%2FfllRXQoAdct33OBUhXTwbxbILlHluYVo%3D&sign_type=RSA&timestamp=2017-12-01+14%3A06%3A59&version=1.0&sign=jooRRQ8BBjJ57KMvYL0vlBrQfXPfopu1tq82TAjqJxBz9d8dddkEdwIGdZeklS7a09l9VTt02J3QLvRS37CpS5k6JfFuYPs%2BBlFs1T1fwYi%2BXITezrrfy%2F0959yE9OynPGIbiqKTb2%2FfllRXQoAdct33OBUhXTwbxbILlHluYVo%3D","success":true}
            //TODO 最后看一下请求返回结果是不是跟上面的一样, 如果不是返回了什么平台号的给赋值一下  response.getBody()
            payTradeInfo.setReturnDetailInfo(JSON.toJSONString(response));
            if (response.isSuccess()) {
                payTradeInfo.setStatus(TradeState.USERPAYING.type());
                payResponse.setSuccessPayResponse(payTradeInfo.getStatus(),response.getBody());
            } else {
                payTradeInfo.setStatus(TradeState.PAYERROR.type());
                payResponse.setFailPayResponse(response.getSubCode(), response.getSubMsg(), payTradeInfo.getStatus(), payTradeInfo.getReturnDetailInfo());
            }
        } else {
            logger.error("tradeType参数错误,当前支付通道不支持此种交易方式." + payTradeInfo.getBizData());
            throw new IllegalArgumentException("tradeType参数错误,当前支付通道不支持此种交易方式.");
        }
        payResponse.setNosPayResponse(payTradeInfo.getTradeNo(), payTradeInfo.getBizNo(), payTradeInfo.getOutTradeNo());
        payTradeInfo.setPlatformPayReturn(JSON.toJSONString(payResponse));
        payTradeInfoMapper.insertSelective(payTradeInfo);
        return ResultModel.ok(payResponse);
    }

    /**
     * 组装阿里支付
     * @param property
     * @return
     */
    @Override
    public AlipayClient aliPayBuild(PayProperty property) {
        AlipayClient alipayClient;
        try {
            alipayClient = new DefaultAlipayClient(AliPay.OPEN_API_DOMAIN, property.getAppId(),
                    property.getPrivateKey(), AliPay.FORMAT,AliPay.CHARSET, property.getPublicKey(),AliPay.SIGN_TYPE);
        } catch (Exception e) {
            logger.error("获取公司支付配置信息失败,参数:" + JSON.toJSONString(property), e);
            throw new IllegalArgumentException("获取公司支付配置信息失败: " + e.getMessage());
        }
        return alipayClient;
    }

    /**
     * 微信
     * @param params
     * @param payTradeInfo
     * @return
     */
    private ResultModel wepay(Map<String, Object> params, PayTradeInfo payTradeInfo, PayProperty property){
        PayResponse payResponse = new PayResponse();
        //组装配置
        Wepay wepay = wePayBuild(property);
        params.put("payNotifyUrl", weNotifyUrl);
        if (payTradeInfo.getTradeType().equals(TradeType.MICROPAY.type())) {
            //刷卡支付
            MicroPayRequest microPayRequest = new MicroPayRequest();
            microPayRequest.setBody(payTradeInfo.getBody());
            microPayRequest.setOutTradeNo(payTradeInfo.getTradeNo());
            microPayRequest.setTotalFee(payTradeInfo.getAmount());
            microPayRequest.setAuthCode(params.get("authCode").toString());
            microPayRequest.setTradeType(TradeType.MICROPAY);
            microPayRequest.setTimeStart(DateUtil.getNowTime("yyyyMMddHHmmss"));
            microPayRequest.setTimeExpire(DateUtil.turnDateToString(DateUtil.getTimeByMinute(new Date(),25),"yyyyMMddHHmmss"));
            microPayRequest.setClientId(params.get("clientId").toString());
            MicroPayResponse response;
            try {
                response = wepay.pay().microPay(microPayRequest);
            } catch (Exception e) {
                logger.error("生成支付订单错误,调用第三方异常." + payTradeInfo.getBizData(), e);
                throw new NullPointerException("生成支付订单错误,调用第三方异常." + e.getMessage());
            }
            //这里开始记录TradeManagerLog
            //{"appId":"wxa1939d25a3f1f441","attach":"","bankType":"COMM_CREDIT","cashFee":"1","cashFeeType":"CNY","feeType":"CNY","is_subscribe":"Y","mchId":"1284695101","nonceStr":"b2gof4btopqb2z21","openId":"oI9rzjoIjgDfvqNNeHm9bEDLuttc","outTradeNo":"201712010000000004","resultCode":"SUCCESS","returnCode":"SUCCESS","returnMsg":"OK","sign":"5C70B45EC3F63AD55E2C0CF89F4455A3","timeEnd":"2 0171201103949","totalFee":"1","tradeType":"MICROPAY","transactionId":"4200000003201712018212600332"}
            //创建返回内容
            payTradeInfo.setReturnDetailInfo(JSON.toJSONString(response));
            payTradeInfo.setOutTradeNo(response.getTransactionId());
            if("SUCCESS".equals(response.getReturnCode())){
                if("SUCCESS".equals(response.getResultCode())){
                    //完全成功了   这种情况不用进行回调通知了
                    payTradeInfo.setNotifieFlag(1);
                    payTradeInfo.setPayTime(DateUtil.turnStringToDate(response.getTimeEnd(),"yyyyMMddHHmmss"));
                    payTradeInfo.setStatus(TradeState.SUCCESS.type());
                    payResponse.setSuccessPayResponse(payTradeInfo.getStatus(),payTradeInfo.getReturnDetailInfo());
                }else{
                    if("SYSTEMERROR".equals(response.getErrCode()) || "BANKERROR".equals(response.getErrCode()) || "USERPAYING".equals(response.getErrCode())) {
                        payTradeInfo.setStatus(TradeState.USERPAYING.type());
                        payResponse.setFailPayResponse(response.getErrCode(), "微信支付错误信息:"+response.getErrCodeDes()+".当前订单交易状态未知,需要等待微信确认后等待回调通知,也可以主动调用查询接口查询!", payTradeInfo.getStatus(), payTradeInfo.getReturnDetailInfo());
                    }else{
                        payTradeInfo.setStatus(TradeState.PAYERROR.type());
                        payResponse.setFailPayResponse(response.getErrCode(), response.getErrCodeDes(), payTradeInfo.getStatus(), payTradeInfo.getReturnDetailInfo());
                    }
                }
            }else{
                payTradeInfo.setStatus(TradeState.PAYERROR.type());
                payResponse.setFailPayResponse(response.getReturnCode(), response.getReturnMsg(), payTradeInfo.getStatus(), payTradeInfo.getReturnDetailInfo());
            }
        } else if(payTradeInfo.getTradeType().equals(TradeType.NATIVE.type())){
            //扫码支付
            QrPayRequest request = new QrPayRequest();
            request.setBody(payTradeInfo.getBody());
            request.setTotalFee(payTradeInfo.getAmount());
            request.setOutTradeNo(payTradeInfo.getTradeNo());
            request.setClientId(params.get("clientId").toString());
            request.setTimeStart(DateUtil.getNowTime("yyyyMMddHHmmss"));
            request.setTimeExpire(DateUtil.turnDateToString(DateUtil.getTimeByMinute(new Date(),25),"yyyyMMddHHmmss"));
            request.setNotifyUrl(weNotifyUrl);
            request.setTradeType(TradeType.NATIVE);
            request.setProductId(request.getBody());
            Map<String, Object> returnMap = null;
            try {
                //{"nonce_str":"GxOw3mIMMQilIjU0","code_url":"weixin://wxpay/bizpayurl?pr=8cP8kxe","appid":"wxa1939d25a3f1f441","sign":"762BBFB6F08412D8D0B9D75611DE9543","trade_type":"NATIVE","return_msg":"OK","result_code":"SUCCESS","mch_id":"1284695101","return_code":"SUCCESS","prepay_id":"wx1913064013704593a01e23f23572598741"}
                returnMap = wepay.pay().qrPay(request);
            } catch (Exception e) {
                logger.error("生成支付订单错误,调用第三方异常." + payTradeInfo.getBizData(), e);
                throw new NullPointerException("生成支付订单错误,调用第三方异常." + e.getMessage());
            }
            payTradeInfo.setReturnDetailInfo(JSON.toJSONString(returnMap));
//            payTradeInfo.setOutTradeNo(returnMap.get());
            if ("SUCCESS".equals(returnMap.get("return_code").toString())) {
                if ("SUCCESS".equals(returnMap.get("result_code").toString())) {
                    payTradeInfo.setStatus(TradeState.USERPAYING.type());
                    payResponse.setSuccessPayResponse(payTradeInfo.getStatus(),(String)returnMap.get("code_url"));
                } else {//业务失败
                    payTradeInfo.setStatus(TradeState.PAYERROR.type());
                    payResponse.setFailPayResponse((String)returnMap.get("err_code"), (String)returnMap.get("err_code_des"), payTradeInfo.getStatus(), payTradeInfo.getReturnDetailInfo());
                }
            } else {//第一层通信失败
                payTradeInfo.setStatus(TradeState.PAYERROR.type());
                payResponse.setFailPayResponse((String)returnMap.get("return_code"), (String)returnMap.get("return_msg"), payTradeInfo.getStatus(), payTradeInfo.getReturnDetailInfo());
            }
        } else if (payTradeInfo.getTradeType().equals(TradeType.APP.type()) || payTradeInfo.getTradeType().equals(TradeType.MINI.type())) {
            //TODO 等真实测试时候看一下这块返回的参数是否带return_code  与result_code 目前的没有判断这两种状态
            //app支付用，统一下单接口
            //JsPayRequest比AppPayRequest多了个 openId，直接用一个吧
            JsPayRequest payRequest = new JsPayRequest();
            payRequest.setBody(payTradeInfo.getBody());
            payRequest.setTotalFee(payTradeInfo.getAmount());
            payRequest.setOutTradeNo(payTradeInfo.getTradeNo());
            payRequest.setClientId(params.get("clientId").toString());
            payRequest.setTimeStart(DateUtil.getNowTime("yyyyMMddHHmmss"));
            payRequest.setTimeExpire(DateUtil.turnDateToString(DateUtil.getTimeByMinute(new Date(),25),"yyyyMMddHHmmss"));
            payRequest.setNotifyUrl(weNotifyUrl);
            try {
                if (payTradeInfo.getTradeType().equals(TradeType.APP.type())) {
                    AppPayResponse appPayResponse = wepay.pay().appPay(payRequest);
                    payTradeInfo.setReturnDetailInfo(JSON.toJSONString(appPayResponse));
                    payTradeInfo.setStatus(TradeState.USERPAYING.type());
                    payResponse.setSuccessPayResponse(payTradeInfo.getStatus(),JSON.toJSONString(appPayResponse));
                } else if (payTradeInfo.getTradeType().equals(TradeType.MINI.type())) {
                    payRequest.setOpenId((String) params.get("openId"));
                    JsPayResponse jsPayRespons = wepay.pay().jsPay(payRequest);
                    payTradeInfo.setReturnDetailInfo(JSON.toJSONString(payRequest));
                    payTradeInfo.setStatus(TradeState.USERPAYING.type());
                    payResponse.setSuccessPayResponse(payTradeInfo.getStatus(),JSON.toJSONString(payRequest));
                }
            } catch (Exception e) {
                logger.error("生成支付订单错误,调用第三方异常." + payTradeInfo.getBizData(), e);
                throw new NullPointerException("生成支付订单错误,调用第三方异常." + e.getMessage());
            }
        } else {
            logger.error("tradeType参数错误,当前支付通道不支持此种交易方式." + payTradeInfo.getBizData());
            throw new IllegalArgumentException("tradeType参数错误,当前支付通道不支持此种交易方式.");
        }
        payResponse.setNosPayResponse(payTradeInfo.getTradeNo(), payTradeInfo.getBizNo(), payTradeInfo.getOutTradeNo());
        payTradeInfo.setPlatformPayReturn(JSON.toJSONString(payResponse));
        payTradeInfoMapper.insertSelective(payTradeInfo);
        return ResultModel.ok(payResponse);
    }

    /**
     * 组装微信支付
     * @param property
     * @return
     */
    @Override
    public Wepay wePayBuild(PayProperty property) {
        Wepay wepay;
        try {
            wepay = WepayBuilder.newBuilder(property.getAppId(), property.getPrivateKey(), property.getMchId()).build();
        } catch (Exception e) {
            logger.error("获取租户配置失败,参数:"+ JSON.toJSONString(property), e);
            throw new WepayException("获取租户配置失败: "+e.getMessage());
        }
        return wepay;
    }

    @Override
    public ResultModel save(PayTradeInfo entity) {
        payTradeInfoMapper.insertSelective(entity);
        return ResultModel.ok(entity);
    }

    @Override
    public ResultModel update(PayTradeInfo entity) {
        payTradeInfoMapper.updateByPrimaryKeySelective(entity);
        return ResultModel.ok(entity);
    }


    /**
     * 获取当前数据表指定日期的订单数（已加1）
     * @param tableName
     * @param yyyyMMdd 日期，格式为 yyyyMMdd，没有横线的
     * @return
     */
    public int getTableNum(String tableName, String yyyyMMdd){
        Map map = new HashMap();
        map.put("tableName", tableName);
        map.put("yyyyMMdd", yyyyMMdd);
        map.put("returnNum", 0);
        payTradeInfoMapper.selectTabelNo(map);
        return (Integer) map.get("returnNum");
    }

    /**
     * 生成单号，暂时是18位纯数字，以后可以扩展 格式为yyyyMMdd+10位流水号,流水号按序排列，非随机
     * @param tableName
     * @param yyyyMMdd 日期，格式为 yyyyMMdd，没有横线的
     * @return
     */
    public String createTableNo(String tableName, String yyyyMMdd){
        int num = getTableNum(tableName, yyyyMMdd);
        if(num<=0) {
            num = 1;
        }
        // 0 代表前面补充0
        // 4 代表长度为4
        // d 代表参数为正数型
        return  yyyyMMdd + String.format("%010d", num);
    }

    @Override
    @Transactional(readOnly = false)
    public PayTradeInfo queryThird(HashMap<String, Object> params, PayTradeInfo payTradeInfo) {
        if(payTradeInfo==null) {//接口进来的根据支付平台的唯一交易单号查询并比对所属企业是否正确
            Preconditions.checkNotNullAndEmpty((String) params.get("tradeNo"), "tradeNo");
            Preconditions.checkNotNullAndEmpty((String) params.get("companyCode"), "companyCode");
            String code = params.get("companyCode").toString();
            payTradeInfo = findByTradeNo(params.get("tradeNo").toString());
            if (payTradeInfo == null) {
                throw new IllegalArgumentException("查无此交易订单");
            }
            if(!code.equals(payTradeInfo.getCompanyCode())){
                throw new IllegalArgumentException("当前企业编码与订单所属企业不匹配");
            }
        }
        if(!payTradeInfo.getStatus().equals("USERPAYING")){
            return payTradeInfo;
        }
        switch (payTradeInfo.getPayChannle()){
            case 1:
                return alipayQuery(payTradeInfo);
            case 2:
                return wepayQuery(payTradeInfo);
            default:
                throw new IllegalArgumentException("payChannle参数错误, 暂不支持该支付方式");
        }
    }


    /**
     * 支付宝
     * @param payTradeInfo
     * @return
     */
    PayTradeInfo alipayQuery(PayTradeInfo payTradeInfo){
        PayProperty property = payPropertyService.selectByCompanyCodeAndPayChannle(payTradeInfo.getCompanyCode(), payTradeInfo.getPayChannle(),getPayType(payTradeInfo));
        if (property == null) {
            throw new IllegalArgumentException("获取公司支付配置信息失败.查无数据");
        }
        AlipayClient alipayClient = aliPayBuild(property);
        AlipayTradeQueryResponse queryResponse;
        try {
            queryResponse = AliPay.query(alipayClient, payTradeInfo.getTradeNo());
        } catch (AlipayApiException e) {
            throw new WepayException(e.getMessage());
        }
        if(queryResponse.isSuccess()){
            /*
            WAIT_BUYER_PAY（交易创建，等待买家付款）、
            TRADE_CLOSED（未付款交易超时关闭，或支付完成后全额退款）、
            TRADE_SUCCESS（交易支付成功）、
            TRADE_FINISHED（交易结束，不可退款）
             */
            String trade_state = queryResponse.getTradeStatus();
            if("WAIT_BUYER_PAY".equals(trade_state)){
                trade_state = TradeState.USERPAYING.type();
            }else if("TRADE_CLOSED".equals(trade_state)){
                trade_state = TradeState.CLOSED.type();
            }else if("TRADE_SUCCESS".equals(trade_state)){
                trade_state = TradeState.SUCCESS.type();
            }else if("TRADE_FINISHED".equals(trade_state)){
                trade_state = TradeState.SUCCESS.type();
            }
            if(!"USERPAYING".equals(trade_state) && !trade_state.equals(payTradeInfo.getStatus())) {
                payTradeInfo.setUpdateTime(new Date());
                payTradeInfo.setStatus(trade_state);
                payTradeInfo.setOutTradeNo(queryResponse.getTradeNo());
                payTradeInfo.setNotifieFlag(0);
                payTradeInfo.setNotifieTime(new Date());
                payTradeInfo.setNotifieCount(payTradeInfo.getNotifieCount() == null ? 0 : (payTradeInfo.getNotifieCount() + 1));
                payTradeInfo.setNotifyDetailInfo(JSON.toJSONString(queryResponse));
                payTradeInfo.setPayTime(queryResponse.getSendPayDate());
                update(payTradeInfo);
            }
        }else{
            if ("ACQ.TRADE_NOT_EXIST".equals(queryResponse.getSubCode())) {
                //这块查询不存在的直接将数据库的交易记录设置为关闭  且不通知
                payTradeInfo.setUpdateTime(new Date());
                payTradeInfo.setStatus(TradeState.CLOSED.type());
//            payTradeInfo.setNotifieFlag(0);
//            payTradeInfo.setNotifieTime(new Date());
//            payTradeInfo.setNotifieCount(payTradeInfo.getNotifieCount() == null ? 0 : (payTradeInfo.getNotifieCount() + 1));
                payTradeInfo.setNotifyDetailInfo(JSON.toJSONString(queryResponse));
                update(payTradeInfo);
            } else {
                throw new WepayException("查询失败!"+queryResponse.getMsg());
            }
        }
        return payTradeInfo;
    }

    /**
     * 微信
     * @param payTradeInfo
     * @return
     */
    PayTradeInfo wepayQuery(PayTradeInfo payTradeInfo){
        PayProperty property = payPropertyService.selectByCompanyCodeAndPayChannle(payTradeInfo.getCompanyCode(), payTradeInfo.getPayChannle(),null);
        if (property == null) {
            throw new IllegalArgumentException("获取公司支付配置信息失败.查无数据");
        }
        WePayOrder wePayOrder = null;
        if (payTradeInfo != null) {
            Wepay wepay = wePayBuild(property);
            wePayOrder = wepay.order().queryByOutTradeNo(payTradeInfo.getTradeNo());
        }
        if (wePayOrder != null) {
            /*
            SUCCESS—支付成功
            REFUND—转入退款
            NOTPAY—未支付
            CLOSED—已关闭
            REVOKED—已撤销（刷卡支付）
            USERPAYING--用户支付中
            PAYERROR--支付失败(其他原因，如银行返回失败)
             */
            String trade_state = "";
            if(!TradeState.NOTPAY.equals(wePayOrder.getTradeState()) && !TradeState.USERPAYING.equals(wePayOrder.getTradeState()) && !TradeState.ACCEPT.equals(wePayOrder.getTradeState())){
                if(TradeState.SUCCESS.equals(wePayOrder.getTradeState())){
                    trade_state = TradeState.SUCCESS.type();
                }else if(TradeState.REFUND.equals(wePayOrder.getTradeState())){
                    trade_state = TradeState.CLOSED.type();
                }else if(TradeState.CLOSED.equals(wePayOrder.getTradeState()) || TradeState.REVOKED.equals(wePayOrder.getTradeState())){
                    trade_state = TradeState.CLOSED.type();
                }else if(TradeState.PAYERROR.equals(wePayOrder.getTradeState())){
                    trade_state = TradeState.PAYERROR.type();
                }
                if(!trade_state.equals(payTradeInfo.getStatus())) {
                    payTradeInfo.setStatus(trade_state);
                    payTradeInfo.setUpdateTime(new Date());
                    payTradeInfo.setOutTradeNo(wePayOrder.getTransactionId());
                    payTradeInfo.setNotifieFlag(0);
                    payTradeInfo.setNotifieTime(new Date());
                    payTradeInfo.setNotifieCount(payTradeInfo.getNotifieCount() == null ? 0 : (payTradeInfo.getNotifieCount() + 1));
                    payTradeInfo.setNotifyDetailInfo(JSON.toJSONString(wePayOrder));
                    payTradeInfo.setPayTime(wePayOrder.getTimeEnd());
                    update(payTradeInfo);
                }
            } else {
                if(TradeState.REVOKED.equals(wePayOrder.getTradeState())){
                    payTradeInfo.setStatus(TradeState.CLOSED.type());
                    payTradeInfo.setUpdateTime(new Date());
                    update(payTradeInfo);
                }else if(TradeState.NOTPAY.equals(wePayOrder.getTradeState())){
                    if (DateUtil.getTimeDifferenceSencond(new Date(), payTradeInfo.getCreateTime()) > 60*30) {//超过30分钟的查询才变成关闭状态
                        payTradeInfo.setStatus(TradeState.CLOSED.type());
                        payTradeInfo.setUpdateTime(new Date());
                        update(payTradeInfo);
                    }
                }
            }
        }
        return payTradeInfo;
    }


    @Override
    public boolean cancelOrderByOutTradeNo(PayTradeInfo payTradeInfo) {
        if (payTradeInfo != null) {
            PayProperty property = payPropertyService.selectByCompanyCodeAndPayChannle(payTradeInfo.getCompanyCode(), payTradeInfo.getPayChannle(),getPayType(payTradeInfo));
            if (property == null) {
                throw new IllegalArgumentException("获取公司支付配置信息失败.查无数据");
            }
            if(payTradeInfo.getPayChannle().equals(1)){//支付宝
                AlipayClient alipayClient = aliPayBuild(property);
                try {
                    return AliPay.cancel(alipayClient, payTradeInfo.getTradeNo());
                } catch (AlipayApiException e) {
                    e.printStackTrace();
                    return false;
                }

            }else if(payTradeInfo.getPayChannle().equals(2)){//微信
                Wepay wepay = wePayBuild(property);
                Map map;
                try {
                    map = wepay.order().cancelOrder(payTradeInfo.getTradeNo());
                } catch (Exception e) {
                    return false;
                }
                if (map != null && TradeState.SUCCESS.type().equals(map.get("result_code")+"")) {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public String wePayNotifies(String notifyXml) {
        //time_end 支付完成时间  格式为yyyyMMddHHmmss
        Map<String, Object> notifyParams = XmlReaders.readXMLString(notifyXml);//Maps.toMap(notifyXml);
        String resultCode = (String) notifyParams.get("result_code");
        String transactionId = (String) notifyParams.get("transaction_id");

        PayTradeInfo payTradeInfo = findByTradeNo((String) notifyParams.get("out_trade_no"));
        if (payTradeInfo != null) {
            if(!payTradeInfo.getStatus().equals(TradeState.USERPAYING.type())){
                return "success";
            }
            PayProperty property = payPropertyService.selectByCompanyCodeAndPayChannle(payTradeInfo.getCompanyCode(), payTradeInfo.getPayChannle(),getPayType(payTradeInfo));
            if (property == null) {
                throw new IllegalArgumentException("获取公司支付配置信息失败.查无数据");
            }
            Wepay wepay = wePayBuild(property);
            if (wepay.notifies().verifySign(notifyParams)) {//验证通过
                Date payTime = null;
                if(notifyParams.get("time_end")!=null){
                    payTime = DateUtil.turnStringToDate(notifyParams.get("time_end").toString(), "yyyyMMddHHmmss");
                }
                //TODO 这块需要联调,现有的文档查不到了
                payTradeInfo.setOutTradeNo(transactionId);
                payTradeInfo.setUpdateTime(new Date());
                payTradeInfo.setPayTime(payTime);
                payTradeInfo.setNotifyDetailInfo(notifyXml);
                if (TradeState.SUCCESS.type().equals(resultCode)) {
                    payTradeInfo.setStatus(TradeState.SUCCESS.type());
                } else if (TradeState.FAIL.type().equals(resultCode)) {
                    payTradeInfo.setStatus(TradeState.PAYERROR.type());
                } else if(TradeState.CLOSED.type().equals(resultCode)){
                    payTradeInfo.setStatus(TradeState.CLOSED.type());
                }
                payTradeInfo.setNotifieFlag(0);
                payTradeInfo.setNotifieTime(new Date());
                payTradeInfo.setNotifieCount(0);
                update(payTradeInfo);
                return wepay.notifies().ok();
            } else {
                logger.error("verify sign failed: {}", notifyXml);
                return wepay.notifies().notOk("签名失败");
            }
        }
        return null;
    }

    @Override
    public String aliPayNotifies(TreeMap<String, String> requestParams) {
        /**
         * gmt_payment 交易付款时间 格式为yyyy-MM-dd HH:mm:ss
         * 支付宝回调参数：
         * {"body":"飞扬FY-718高性能82列平推票据打印机等商品","subject":"商城下单支付","sign_type":"RSA2","buyer_logon_id":"347***@qq.com","auth_app_id":"2017112900244862","notify_type":"trade_status_sync","out_trade_no":"201712080000000004","point_amount":"0.00","version":"1.0","fund_bill_list":"[{\"amount\":\"0.01\",\"fundChannel\":\"ALIPAYACCOUNT\"}]","buyer_id":"2088102146582094","total_amount":"0.01","trade_no":"2017120821001004090529521912","notify_time":"2017-12-08 10:37:06","charset":"utf-8","invoice_amount":"0.01","trade_status":"TRADE_SUCCESS","gmt_payment":"2017-12-08 10:37:06","sign":"pUMQEcMQbbbe54uihslq4dgOJu9cqvJw2T6L4ky9mN+mpWcRnuBlgDHqbXZP2yLjZFvX6yIehKw+3KaGYsxLHws464+zB+Fs4wN+bo+xH62lnbFjRx5OlmQw0Zj7oHZdhD0Oo8Sdn8PdaNSV8QwTENH3bSeEu8Q2a2cW2jRwCiftVSy4VyZDoGiFQJNsw4dSPeTOjXDL3L/OkE6hI94sLRv6FiIEw6OUnPZuk6/buUUtcI9CB4SjsoKOg1Gr18Vc3N3o8dQ586mdJ+Ud0PdMUvYWQ9L5G2tXY61rDTfQHEa8zjhA4oMHqElec8q3iZjt7O82zYyhfU40KtbsafZqww==","gmt_create":"2017-12-08 10:37:05","buyer_pay_amount":"0.01","receipt_amount":"0.01","app_id":"2017112900244862","seller_id":"2088421609420013","notify_id":"e90d7ee85fb0e355926e7a4aa50eee6gp5","seller_email":"2820173973@qq.com"}
         */
        /*
        第一步： 在通知返回参数列表中，除去sign、sign_type两个参数外，凡是通知返回回来的参数皆是待验签的参数。
        第二步： 将剩下参数进行url_decode, 然后进行字典排序，组成字符串，得到待签名字符串：
        第三步： 将签名参数（sign）使用base64解码为字节码串。
        第四步： 使用RSA的验签方法，通过签名字符串、签名参数（经过base64解码）及支付宝公钥验证签名。
         */
        String transactionId = requestParams.get("trade_no");
        PayTradeInfo payTradeInfo = findByTradeNo(requestParams.get("out_trade_no"));
        if (payTradeInfo != null) {
            if(!payTradeInfo.getStatus().equals(TradeState.USERPAYING.type())){
                return "success";
            }
            PayProperty property = payPropertyService.selectByCompanyCodeAndPayChannle(payTradeInfo.getCompanyCode(), payTradeInfo.getPayChannle(),getPayType(payTradeInfo));
            if (property == null) {
                throw new IllegalArgumentException("获取公司支付配置信息失败.查无数据");
            }
            boolean flag;
            Map<String, String> signParams = new HashMap<>(requestParams);
            try {
                flag = AlipaySignature.rsaCheckV1(signParams, property.getPublicKey(), AliPay.CHARSET, AliPay.SIGN_TYPE);
            } catch (AlipayApiException e) {
                e.printStackTrace();
                logger.error("支付宝验签失败", e);
                return "验签失败";
            }
            if(flag){
                /*
                第五步：在步骤四验证签名正确后，必须再严格按照如下描述校验通知数据的正确性。
                1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
                2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
                3、校验通知中的seller_id（或者seller_email) 是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
                4、验证app_id是否为该商户本身。
                上述1、2、3、4有任何一个验证不通过，则表明本次通知是异常通知，务必忽略。
                在上述验证通过后商户必须根据支付宝不同类型的业务通知，正确的进行不同的业务处理，并且过滤重复的通知结果数据。
                在支付宝的业务通知中，只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时，支付宝才会认定为买家付款成功。
                 */
                if(payTradeInfo.getAmount().equals(new Double(requestParams.get("total_amount")).intValue())){
                    logger.error("支付金额不等: {}", requestParams);
                    return "支付金额不等";
                }
                if(!property.getAppId().equals(requestParams.get("app_id"))){
                    logger.error("appId不等: {}", requestParams);
                    return "appId不等";
                }
                Date payTime = null;
                if(requestParams.get("gmt_payment")!=null){
                    payTime = DateUtil.turnStringToDate(requestParams.get("gmt_payment").toString(), "yyyy-MM-dd HH:mm:ss");
                }
                payTradeInfo.setOutTradeNo(transactionId);
                payTradeInfo.setUpdateTime(new Date());
                payTradeInfo.setPayTime(payTime);
                payTradeInfo.setNotifyDetailInfo(JSON.toJSONString(requestParams));
                /*
                    TRADE_FINISHED	交易完成	true（触发通知）
                    TRADE_SUCCESS	支付成功	true（触发通知）
                    WAIT_BUYER_PAY	交易创建	false（不触发通知）
                 */
                //只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时，支付宝才会认定为买家付款成功。
                String resultCode = requestParams.get("trade_status");
                if(resultCode.equals("TRADE_FINISHED") || resultCode.equals("TRADE_SUCCESS")){
                    payTradeInfo.setStatus(TradeState.SUCCESS.type());
                }else if(resultCode.equals("TRADE_CLOSED")){
                    payTradeInfo.setStatus(TradeState.CLOSED.type());
                }
                payTradeInfo.setNotifieFlag(0);
                payTradeInfo.setNotifieTime(new Date());
                payTradeInfo.setNotifieCount(0);
                update(payTradeInfo);
                return "success";
            }else{
                logger.error("支付宝验签失败: {}", requestParams);
                return "验签失败";
            }
        }
        return null;
    }

    @Override
    public int updateNotifieStatus(PayTradeInfo p) {
        return payTradeInfoMapper.updateNotifieStatus(p);
    }
}
